Type System

	Const INI_PATH$ = "game.ini"
	
	Global _keyPressed%[256]
	
	Global frameTime%
	Global frameLength%
	Global frameNum%
	
	Global nullStream:RAMStream
	
	Function LoadIni%()
	
		' todo: setup ini variable defaults
		
		Game.playerName$ = "player"
		
		Local iniFile:TStream = ReadFile(INI_PATH)
		
		If iniFile
		
			While Not Eof(iniFile)
			
				Local line$ = ReadLine$(iniFile)
				
				Local tokens:String[] = Tokenize(line$)
				
				If Left$(tokens[0], 1) <> "#"
				
					Select Lower$(tokens[0])
					
						Case "name"
						
							If Len(tokens) = 2
							
								Game.playerName$ = Left$(tokens[1], Game.PLAYER_NAME_LENGTH)
								
							EndIf
						
					End Select
				
				EndIf
				
			Wend
			
			CloseFile(iniFile)
			
		EndIf
		
		Return True
		
	End Function
	
	Function SaveIni%()
	
		Local iniFile:TStream = WriteFile(INI_PATH)
		
		If iniFile
		
			WriteLine(iniFile, "name " + Game.playerName$)
				
			CloseFile(iniFile)
			
		Else
			
			Return False
			
		EndIf
	
		Return True
		
	End Function
	
	Function Init%()
	
		frameTime   = MilliSecs()
		frameLength = 16
		frameNum    = 0
	
		LoadIni()
		
		nullStream = RAMStream.Create(1)
		nullStream.WriteByte(0)
		
		Return True
		
	End Function
	
	Function Update%()
	
		Local newFrameTime% = MilliSecs()
		
		frameLength = newFrameTime - frameTime
		frameTime   = newFrameTime
		
		frameNum = frameNum + 1
		
		For Local k% = 0 To 256 - 1
		
			_keyPressed[k] = False
			
		Next
		
		While PollEvent()
		
			Select EventID()
			
				Case EVENT_MOUSEENTER
				
					HideMouse()
					
				Case EVENT_MOUSELEAVE
				
					ShowMouse()
					
				Case EVENT_KEYDOWN
				
					_keyPressed[EventData()] = True
					
				Case EVENT_KEYREPEAT
				
					_keyPressed[EventData()] = True
					
				Case EVENT_APPTERMINATE
				
					Return False
					
				Case EVENT_WINDOWCLOSE
				
					Return False
					
				Case EVENT_PROCESSEXIT
				
					Return False
			
			End Select
		
		Wend
		
		Return True
		
	End Function
	
	Function Shutdown%()
	
		SaveIni()
		
		Return True
	
	End Function

End Type

Type Resource

	Field path$
	
End Type

Type RAMStream

	Field buffer:Byte Ptr = Null
	
	Field position% = 0
	
	Field size% = 0
	Field capacity% = 0
	
	Const MANAGED_BUFFER% = 1
	
	Field managed% = MANAGED_BUFFER
	
	Field deltaSource:RAMStream = Null
	
	Field bits% = 0
	Field mask% = 0
	
	Field bytes:Byte[8]
	Field numBytes% = 0
	
	Function Create:RAMStream(bufferSize%, _managed% = MANAGED_BUFFER)
	
		Local buf:RAMStream = New RAMStream
		
		buf.CreateBuffer(bufferSize, _managed)
		
		Return buf
		
	End Function
	
	Method CreateBuffer(bufferSize%, _managed% = MANAGED_BUFFER)
	
		FreeBuffer()
		
		managed = _managed
			
		size = bufferSize
		capacity = bufferSize
		
		If managed
		
			buffer = New Byte[bufferSize]
			
		Else
		
			buffer = MemAlloc(bufferSize)
			
		EndIf
		
		Assert buffer <> Null
		
	End Method
	
	Method FreeBuffer()
	
		If Not managed And buffer
		
			MemFree buffer
				
		EndIf
	
		buffer = Null
	
	End Method
	
	Method Clear%(start% = 0, count% = 0)
	
		If count = 0 Then count = size
		
		If start + count > size Then count = size - start
		
		If count > 0 Then MemClear(Varptr buffer[start], count)
		
		Return count
	
	End Method
	
	Method Checksum%(start% = 0, count% = 0)
		
		If start = 0 Then start = position
		If count = 0 Then count = size - start
		
		If start + count > size Then count = size - start
	
		Local crc% = $1E4F589D
			
		For Local b% = start To start + count - 1
		
			crc = ((crc Shl 7) | ((crc & $FE000000) Shr 25)) + (buffer[b] + 1) * ((crc Shr 8) + 1) - 1
			
		Next
		
		Return crc
		
	End Method
	
	Method SetSize%(_size%)
	
		If (_size >= 0 And _size <= capacity)
		
			size = _size
			
		EndIf
		
		Return size	
		
	End Method
	
	Method SetDeltaSource(_deltaSource:RAMStream)
	
		deltaSource = _deltaSource
		
	End Method
	
	Method Read%(buf:Byte Ptr, count%)
		
		Local bytesRead% = count
		
		If position + count > size Then bytesRead = size - position
		
		For Local b% = 0 To bytesRead - 1
		
			If deltaSource <> Null
				
				If bits = 0
				
					mask = buffer[position]
					
					bits = 8
					
				Else
				
					If (mask & $80)
					
						buf[b] = deltaSource.ReadByte() + buffer[position]
						
					Else
					
						buf[b] = deltaSource.ReadByte()
						
						position = position - 1
						
					EndIf
					
					mask = mask Shl 1
				
					bits = bits - 1
					
				EndIf
											
			Else
					
				buf[b] = buffer[position]
				
			EndIf
			
			position = position + 1
			
		Next
		
		Return bytesRead
		
	End Method
	
	Method Write%(buf:Byte Ptr, count%)
	
		Local bytesWritten% = count
		
		If position + count > size Then bytesWritten = size - position
		
		For Local b% = 0 To bytesWritten - 1
		
			If deltaSource <> Null
						
				Local b0:Byte = deltaSource.ReadByte()
				Local b1:Byte = buf[b]
				
				Local d:Byte = b1 - b0
				
				If d = 0
				
					mask = (mask Shl 1)
					
				Else
				
					mask = (mask Shl 1) | 1
					
					bytes[numBytes] = d
					numBytes = numBytes + 1
					
				EndIf
				
				bits = bits + 1
				
				If bits = 8 Then Flush()
											
			Else
			
				buffer[position] = buf[b]
				position = position + 1
				
			EndIf
			
		Next
		
		Return bytesWritten		
	
	End Method
	
	Method Flush%()
	
		Local count% = 0
		
		If bits > 0
			
			count% = numBytes + 1
			
			If position + count > size Then count = size - position
		
			If numBytes > 0
			
				buffer[position] = mask
				
				position = position + 1
				
			EndIf
			
			For Local b% = 0 To count - 1
				
				buffer[position] = bytes[b]
				
				position = position + 1
			
			Next
		
		EndIf
		
		mask = 0
		bits = 0
		
		numBytes = 0
		
		Return count
		
	End Method
	
	Method CopyBytes%(sourceBuffer:RAMStream, count% = 0)
	
		If count = 0 Then count = sourceBuffer.size - sourceBuffer.position
		
		If position + count >= size Then count = size - position
		If sourceBuffer.position + count >= sourceBuffer.size Then count = sourceBuffer.size - sourceBuffer.position
		
		If count > 0 Then MemCopy(Varptr buffer[position], Varptr sourceBuffer.buffer[sourceBuffer.position], count)
		
		Return count

	End Method
	
	Method Seek%(pos%)
	
		If (pos >= 0 And pos <= size)
		
			position = pos
			
		EndIf
		
		Return position

	End Method
	
	Method Eof%()
	
		If position = size
		
			Return True
			
		Else
		
			Return False
			
		EndIf
		
	End Method
	
	Method ReadInt%()
	
		Local val% = 0	
		Read(Varptr val, 4)
		
		Return val
	
	End Method
	
	Method ReadShort:Short()
	
		Local val:Short = 0	
		Read(Varptr val, 2)
		
		Return val
	
	End Method
	
	Method ReadByte:Byte()
	
		Local val:Byte = 0	
		Read(Varptr val, 1)
		
		Return val
	
	End Method
	
	Method ReadFloat#()
	
		Local val# = 0.0
		Read(Varptr val, 4)
		
		Return val#
		
	End Method
	
	Method WriteInt%(val%)
	
		Return Write(Varptr val, 4)
	
	End Method
	
	Method WriteString%(text$)
	
		Local bytesWritten% = Write(text.ToCString(), Len(text) + 1)
		
		Return bytesWritten
	
	End Method
	
	Method ReadString$()
	
		Local myString$ = ""
		
		Local bytesRead% = 1
		
		Repeat
		
			Local myChar:Byte = 0
			
			bytesRead% = Read(Varptr myChar, 1)
			
			If myChar
			
				myString$ = myString$ + Chr$(myChar)
				
			Else
			
				Return myString$
				
			EndIf
		
		Until bytesRead = 0
		
		Return myString$
		
	End Method
	
	Method WriteShort%(val:Short)
	
		Return Write(Varptr val, 2)
	
	End Method
	
	Method WriteByte%(val:Byte)

		Return Write(Varptr val, 1)
	
	End Method
	
	Method WriteFloat%(val#)
		
		Return Write(Varptr val#, 4)
		
	End Method		
	
	Method WritePackedFloat32%(val#)
	
		Local frac:Byte = Byte((val# - Floor(val#)) * 255.0)
		Local whole:Int = Int(Floor(val#)) Shl 8
		
		Return WriteInt(Int(whole | frac))
		
	End Method
	
	Method WritePackedFloat16%(val#)

		Local frac:Byte = Byte((val# - Floor(val#)) * 255.0)
		Local whole:Short = Byte(Floor(val#)) Shl 8
		
		Return WriteShort(Short(whole | frac))
		
	End Method
	
	Method WritePackedFloat8%(val#)

		Local frac:Byte = Byte((val# - Floor(val#)) * 255.0)
		
		Return WriteByte(frac)
		
	End Method
	
	Method ReadPackedFloat32#()
	
		Local val% = ReadInt()
		
		Local frac# = Float(val & $000000FF) / 255.0
		Local whole# = Float((val & $FFFFFF00) Shr 8)
		
		Return whole# + frac#
		
	End Method
	
	Method ReadPackedFloat16#()

		Local val:Short = ReadShort()
		
		Local frac# = Float(val & $00FF) / 255.0
		Local whole# = Float((val & $FF00) Shr 8)
		
		Return whole# + frac#
		
	End Method
	
	Method ReadPackedFloat8#()
		
		Local val:Byte = ReadByte()
		
		Local frac# = Float(val) / 255.0
		
		Return frac#
		
	End Method
		
End Type

Function Tokenize:String[](text$)

	Local tokens:String[]
	
	Local token$ = ""
	
	For Local c% = 1 To Len(text$)
	
		Local char$ = Mid$(text$, c, 1)
		
		If char$ = " " Or Asc(char$) = 9 'tab
		
			If token$ <> ""
			
				tokens = tokens[..Len(tokens) + 1]
				tokens[Len(tokens) - 1] = token$
				
				token$ = ""
				
			EndIf
			
		Else
		
			token$ = token$ + char$
			
		EndIf
		
	Next
	
	If token$ <> "" Or Len(tokens) = 0
	
		tokens = tokens[..Len(tokens) + 1]
		tokens[Len(tokens) - 1] = token$
		
	EndIf
		
	Return tokens

End Function

Function KeyPressed%(key%)

	Return System._keyPressed[key]
	
End Function